package cn.cosmosx.base.jackson;

import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;

import java.io.IOException;
import java.lang.annotation.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Currency;
import java.util.List;
import java.util.Objects;

/**
 * 大数格式化注解，需配合{@linkplain DecimalFormat}使用
 * value可参照{@linkplain DecimalFormat}
 *
 * @author gengzhy
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = BigDecimalFormat.Serializer.class)
@JsonDeserialize(using = BigDecimalFormat.Deserializer.class)
public @interface BigDecimalFormat {
    /**
     * 格式化字符串
     * <pre>
     * 逗号分隔：#,###.##(1234.99->1,234.99)
     * 百分数：#.###%（0.125->12.5%）
     * 千分数（‰或\u2030）：#.###‰（0.125->125‰）
     * 货币（¤或\u00A4）：¤#。##（0.12->¥0.12），不同币种，需搭配不同国家地区使用
     * </pre>
     */
    String value() default "#.##";

    /**
     * 丢弃百分号或千分号（只有在{@linkplain BigDecimalFormat#value()}以百分号或千分号结尾时生效）
     */
    boolean discardPercentSign() default false;

    /**
     * 地区{@linkplain BigDecimalFormat#value()}带格式化货币符号时生效，默认跟随当前地区
     */
    Location local() default Location.NONE;

    enum Location {
        NONE, CN, US, UK, FR, JP
    }

    class Serializer extends JsonSerializer<BigDecimal> implements ContextualSerializer {
        private final static List<Character> PERCENT_SIGNS = Arrays.asList('%', '‰');

        /**
         * 格式化字符串
         */
        private String format = "#.##";

        /**
         * 是否丢弃百分号
         */
        private boolean discardPercentSign = false;

        private Location local = Location.CN;

        @Override
        public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            DecimalFormat decimalFormat = new DecimalFormat(format);
            decimalFormat.setCurrency(Currency.getInstance(getLocal(local)));
            String val = decimalFormat.format(value);
            if (discardPercentSign && PERCENT_SIGNS.contains(format.charAt(format.length() - 1))) {
                val = val.replace("%", "").replace("‰", "");
            }
            gen.writeString(val);
        }

        @Override
        public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
            if (property == null) {
                return prov.findNullValueSerializer(null);
            }
            if (!Objects.equals(property.getType().getRawClass(), BigDecimal.class)) {
                return prov.findValueSerializer(property.getType(), property);
            }
            Serializer serializer = new Serializer();
            BigDecimalFormat formatAnno = property.getAnnotation(BigDecimalFormat.class);
            if (formatAnno == null) {
                formatAnno = property.getContextAnnotation(BigDecimalFormat.class);
            }
            if (formatAnno != null) {
                serializer.format = formatAnno.value();
                serializer.discardPercentSign = formatAnno.discardPercentSign();
                serializer.local = formatAnno.local();
            }
            return serializer;
        }

        private java.util.Locale getLocal(Location local) {
            java.util.Locale l;
            switch (local) {
                case CN:
                    l = java.util.Locale.CHINA;
                    break;
                case US:
                    l = java.util.Locale.US;
                    break;
                case UK:
                    l = java.util.Locale.UK;
                    break;
                case FR:
                    l = java.util.Locale.FRANCE;
                    break;
                case JP:
                    l = java.util.Locale.JAPAN;
                    break;
                default:
                    l = java.util.Locale.getDefault();
            }
            return l;
        }
    }

    class Deserializer extends JsonDeserializer<BigDecimal> {

        @Override
        public BigDecimal deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
            String value = p.getText().trim();
            if (ObjectUtil.isEmpty(value)) {
                return null;
            }
            // 去掉逗号和空格分隔符
            value = value.replace(" ", "").replace(",", "");
            // 替换百分号，千分号
            if (value.endsWith("%")) {
                return new BigDecimal(value.replace("%", "")).divide(new BigDecimal("100"), 6, RoundingMode.HALF_UP);
            }
            if (value.endsWith("‰")) {
                return new BigDecimal(value.replace("‰", "")).divide(new BigDecimal("1000"), 8, RoundingMode.HALF_UP);
            }
            return new BigDecimal(value);
        }
    }
}
